[READ-ONLY] Mirror of https://github.com/flo-bit/skywatched. review movies and tv shows, based on at proto skywatched.app
0

Configure Feed

Select the types of activity you want to include in your feed.

skywatched / src / routes / [kind=item] / [id] / +page.svelte
6.2 kB 181 lines
1<script lang="ts"> 2 import { type PageData } from './$types'; 3 import { cn, nameToId } from '$lib/utils'; 4 import { rateMovieModal } from '$lib/state/modals.svelte'; 5 import { videoPlayer } from '$lib/state/video.svelte'; 6 import { watchedItems } from '$lib/state/user.svelte'; 7 8 import Container from '$lib/Components/Layout/Container.svelte'; 9 import ItemsList from '$lib/Components/Items/ItemsList.svelte'; 10 import ReviewList from '$lib/Components/Items/ReviewList.svelte'; 11 import Avatar from '$lib/Components/User/Avatar.svelte'; 12 import { page } from '$app/stores'; 13 14 let { data }: { data: PageData } = $props(); 15</script> 16 17<svelte:head> 18 <title>{data.item.title} | skywatched</title> 19 20 <meta name="description" content={`Rate and review "${data.item.title}" on skywatched`} /> 21 22 <meta property="og:url" content={$page.url.href} /> 23 <meta property="og:type" content="website" /> 24 <meta property="og:title" content="{data.item.title} | skywatched.app" /> 25 <meta property="og:description" content={`Rate and review "${data.item.title}" on skywatched`} /> 26 <meta property="og:image" content="{$page.url.href}/og.png" /> 27 28 <meta name="twitter:card" content="summary_large_image" /> 29 <meta property="twitter:domain" content="skywatched.app" /> 30 <meta property="twitter:url" content={$page.url.href} /> 31 <meta name="twitter:title" content="{data.item.title} | skywatched.app" /> 32 <meta name="twitter:description" content={`Rate and review "${data.item.title}" on skywatched`} /> 33 <meta name="twitter:image" content="{$page.url.href}/og.png" /> 34</svelte:head> 35 36{#snippet buttons()} 37 {#if data.user && !watchedItems.hasRated(data.item)} 38 <button 39 class={cn( 40 'inline-flex items-center gap-2 rounded-md bg-white/10 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm transition-all duration-75 hover:bg-white/20 ', 41 watchedItems.hasRated(data.item) 42 ? 'bg-green-500/10 text-green-400 hover:bg-green-500/20' 43 : '' 44 )} 45 onclick={() => { 46 rateMovieModal.show({ 47 ...data.item 48 }); 49 }} 50 > 51 rate {data.kind === 'movie' ? 'movie' : 'show'} 52 </button> 53 {/if} 54 {#if data.trailer} 55 <button 56 onclick={() => videoPlayer.show(data.trailer ?? '')} 57 type="button" 58 class="inline-flex w-fit items-center gap-x-1.5 rounded-md border border-accent-500/30 bg-accent-700/20 px-3 py-2 text-sm font-semibold text-accent-400 shadow-sm transition-all duration-100 hover:bg-accent-700/30 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-600" 59 > 60 <svg 61 xmlns="http://www.w3.org/2000/svg" 62 viewBox="0 0 24 24" 63 fill="currentColor" 64 class="-ml-0.5 size-5" 65 > 66 <path 67 d="M4.5 4.5a3 3 0 0 0-3 3v9a3 3 0 0 0 3 3h8.25a3 3 0 0 0 3-3v-9a3 3 0 0 0-3-3H4.5ZM19.94 18.75l-2.69-2.69V7.94l2.69-2.69c.944-.945 2.56-.276 2.56 1.06v11.38c0 1.336-1.616 2.005-2.56 1.06Z" 68 /> 69 </svg> 70 71 Trailer 72 </button> 73 {/if} 74{/snippet} 75 76<img 77 src="https://image.tmdb.org/t/p/w780{data.item.backdrop_path}" 78 alt="" 79 class="fixed h-full w-full object-cover object-center opacity-20" 80/> 81<div class="fixed inset-0 h-full w-full bg-black/50"></div> 82 83<Container class="relative z-10 pb-8 pt-4"> 84 <div class="flex gap-4 px-4 pt-8"> 85 <img 86 src="https://image.tmdb.org/t/p/w500{data.item.poster_path}" 87 alt="" 88 class="poster h-36 w-24 shrink-0 rounded-lg border border-white/10 sm:h-64 sm:w-44" 89 style:--name={`poster-${data.item.ref}`} 90 /> 91 <div class="flex flex-col gap-4"> 92 <div 93 class="title max-w-xl text-3xl font-semibold text-white sm:text-4xl" 94 style:--name={`title-${data.item.ref}`} 95 > 96 {data.item.title ?? data.item.name} 97 </div> 98 99 {#if data.settings?.streaming_region?.code && data.watchProviders[data.settings.streaming_region.code]?.flatrate} 100 <div class="mt-2 text-sm text-white sm:mt-4"> 101 <div class="mb-2 flex flex-wrap gap-4 text-xs font-medium"> 102 stream on 103 <span class="flex items-center gap-2 text-base-400" 104 >from <a 105 href="https://www.justwatch.com" 106 target="_blank" 107 class="text-base-300 hover:text-accent-300" 108 > 109 <img src="/justwatch_logo.svg" alt="justwatch" class="h-3" /> 110 </a></span 111 > 112 </div> 113 <a 114 href={data.watchProviders[data.settings.streaming_region.code].link} 115 target="_blank" 116 class="flex flex-wrap gap-2" 117 > 118 {#each data.watchProviders[data.settings.streaming_region.code].flatrate as provider} 119 <img 120 src="https://image.tmdb.org/t/p/w500{provider.logo_path}" 121 alt={provider.provider_name} 122 class="size-8 rounded-md border border-base-800 md:size-12" 123 /> 124 {/each} 125 </a> 126 </div> 127 {/if} 128 <div class="hidden gap-2 sm:flex"> 129 {@render buttons()} 130 </div> 131 </div> 132 </div> 133 134 <div class="px-4 pt-4 text-sm text-white"> 135 <div class="mb-4 flex gap-2 sm:hidden"> 136 {@render buttons()} 137 </div> 138 <div class="mb-4 max-w-2xl text-sm text-white"> 139 <div class="mb-2 text-lg font-semibold">overview</div> 140 {data.item.overview} 141 </div> 142 </div> 143 144 {#if data.ratings.length > 0} 145 <div class="mt-8 px-4 text-lg font-semibold">reviews</div> 146 147 <ReviewList reviews={data.ratings} showMovieDetails={false} class="" /> 148 {/if} 149 150 {#if data.recommendations.length > 0} 151 <div class="px-4 pt-4 text-sm text-white"> 152 <div class="mb-2 text-lg font-semibold">recommendations</div> 153 154 <ItemsList items={data.recommendations} showMark={!!data.user} /> 155 </div> 156 {/if} 157 158 {#if data.cast.length > 0} 159 <div class="px-4 pb-8 pt-4 text-sm text-white"> 160 <div class="mb-2 text-lg font-semibold">cast</div> 161 162 <div class={cn('flex gap-x-6 overflow-x-auto')}> 163 {#each data.cast as castMember} 164 <a 165 href={`/cast/${castMember.id}-${nameToId(castMember.name)}`} 166 class="flex flex-col items-center gap-1" 167 > 168 <Avatar 169 src={castMember.profile_path 170 ? 'https://image.tmdb.org/t/p/w500' + castMember.profile_path 171 : undefined} 172 size="size-32" 173 /> 174 <div class="text-center text-xs font-medium">{castMember.name}</div> 175 <div class="text-center text-xs text-base-400">{castMember.character}</div> 176 </a> 177 {/each} 178 </div> 179 </div> 180 {/if} 181</Container>